﻿using System;

using Conf = OCXS.OCXSServiceLibrary.Configuration;

namespace OCXS.OCXSServiceLibrary.OCXSEnvironment {

    /// <summary>
    /// Root class for synthetizers.
    /// </summary>
    /// <remarks>
    /// This class cannot be directly instantiated.
    /// </remarks>
    public abstract class Synthetizer : ISynth {

        /// <summary>
        /// Max value for time.
        /// </summary>
        /// <remarks>
        /// This upper bound is used to limit internal
        /// simulated time representation.
        /// </remarks>
        public const int MaxTime = 1000;

        /// <summary>
        /// Internal time.
        /// </summary>
        /// <remarks>
        /// Every synthetizer uses a personal independent simulated time flow in order to
        /// emulate the generation of new values caused by a real system.
        /// </remarks>
        protected int time;

        /// <summary>
        /// Static values to be used to be used for variable generation.
        /// </summary>
        /// <remarks>
        /// This is used by those variables which have a sequence specified in configuration file.
        /// </remarks>
        protected float[] sequence;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <remarks>
        /// Initializs time and sequence to their default values.
        /// </remarks>
        public Synthetizer() {
            this.time = 0;
            this.sequence = null;
        }

        /// <summary>
        /// Resets the internal time.
        /// </summary>
        public void ResetTime() {
            time = 0;
        }

        /// <summary>
        /// Gets the new value and increments time.
        /// </summary>
        /// <returns>The new generated value.</returns>
        public float SynthValue() {

            if (time == MaxTime)
                ResetTime();

            return this.sequence[(time++) % sequence.Length];

        }

        /// <summary>
        /// Returns the instance of the most reasonable generator according to
        /// the specified variable.
        /// </summary>
        /// <param name="v">The variable used to infer the best fitted generator.</param>
        /// <returns>The instance of the most reasonable generator according to
        /// the specified variable.</returns>
        public static Synthetizer GetSynthetizer(Conf.VariableType v) {

            switch (v.VariableT) {

            case Conf.VariableTType.Pressure:
                return PressureSynthetizer.GetInstance(v);

            case Conf.VariableTType.Temperature:
                return TemperatureSynthetizer.GetInstance(v);

            case Conf.VariableTType.Volume:
                return VolumeSynthetizer.GetInstance(v);

            case Conf.VariableTType.Bit:
            default:
                return BitSynthetizer.GetInstance(v);

            }

        }

    } /* Synthetizer */

    /// <summary>
    /// Represents a Synthesizer for Bit-typed variables.
    /// </summary>
    /// <remarks>
    /// This class cannot be constructed. Use the <c>GetInstanceMethod</c> instead.
    /// </remarks>
    public class BitSynthetizer : Synthetizer {

        /// <summary>
        /// Construct a new synthetizer for type Bit.
        /// </summary>
        private BitSynthetizer() : base() {
        }

        /// <summary>
        /// Construct a new synthetizer for type Bit specifying a sequence of values.
        /// </summary>
        /// <param name="seq">The specified sequence.</param>
        private BitSynthetizer(float[] seq) : this() {
            this.sequence = seq;
        }

        /// <summary>
        /// Gets a new instance of the class.
        /// </summary>
        /// <param name="v">The variable used to set up the synthetizer.</param>
        /// <returns>A new instance of the class</returns>
        public static BitSynthetizer GetInstance(Conf.VariableType v) {
            
            float[] seq;

            if (v.VariableGenerator.GeneratingFunctionSpecified) {    

                // Function
                switch (v.VariableGenerator.GeneratingFunction) {
                
                case Conf.GeneratingFunctionType.Cosine:
                    seq = new float[] { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
                    break;

                case Conf.GeneratingFunctionType.Linear:
                    seq = new float[] { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 };
                    break;
                
                case Conf.GeneratingFunctionType.Ramp:
                    seq = new float[] { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };
                    break;

                case Conf.GeneratingFunctionType.Sine:
                    seq = new float[] { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };
                    break;

                case Conf.GeneratingFunctionType.SquareWave:
                    seq = new float[] { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
                    break;

                case Conf.GeneratingFunctionType.Null:
                default:
                    seq = new float[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                    break;

                }

            } else {

                // Static values
                seq = v.VariableGenerator.StaticValues;

            }

            return new BitSynthetizer(seq);
        }

    } /* BitSynthetizer */

    /// Represents a Synthesizer for Bit-typed variables.
    /// </summary>
    /// <remarks>
    /// This class cannot be constructed. Use the <c>GetInstanceMethod</c> instead.
    /// </remarks>
    public class PressureSynthetizer : Synthetizer {

        /// <summary>
        /// Construct a new synthetizer for type Pressure.
        /// </summary>
        private PressureSynthetizer() : base() {
        }

        /// <summary>
        /// Construct a new synthetizer for type Pressure specifying a sequence of values.
        /// </summary>
        /// <param name="seq">The specified sequence.</param>
        private PressureSynthetizer(float[] seq) : this() {
            this.sequence = seq;
        }

        /// <summary>
        /// Gets a new instance of the class.
        /// </summary>
        /// <param name="v">The variable used to set up the synthetizer.</param>
        /// <returns>A new instance of the class</returns>
        public static PressureSynthetizer GetInstance(Conf.VariableType v) {

            float[] seq;

            if (v.VariableGenerator.GeneratingFunctionSpecified) {

                // Function
                switch (v.VariableGenerator.GeneratingFunction) {

                case Conf.GeneratingFunctionType.Cosine:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Linear:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Ramp:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Sine:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.SquareWave:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Null:
                default:
                    seq = new float[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                    break;

                }

            } else {

                // Satic values
                seq = v.VariableGenerator.StaticValues;

            }

            return new PressureSynthetizer(seq);
        }

    } /* PressureSynthetizer */

    /// <summary>
    /// Represents a Synthesizer for Volume-typed variables.
    /// </summary>
    /// <remarks>
    /// This class cannot be constructed. Use the <c>GetInstanceMethod</c> instead.
    /// </remarks>
    public class VolumeSynthetizer : Synthetizer {

        /// <summary>
        /// Construct a new synthetizer for type Volume.
        /// </summary>
        private VolumeSynthetizer() : base() {
        }

        /// <summary>
        /// Construct a new synthetizer for type Volume specifying a sequence of values.
        /// </summary>
        /// <param name="seq">The specified sequence.</param>
        private VolumeSynthetizer(float[] seq) : this() {
            this.sequence = seq;
        }

        /// <summary>
        /// Gets a new instance of the class.
        /// </summary>
        /// <param name="v">The variable used to set up the synthetizer.</param>
        /// <returns>A new instance of the class</returns>
        public static VolumeSynthetizer GetInstance(Conf.VariableType v) {

            float[] seq;

            if (v.VariableGenerator.GeneratingFunctionSpecified) {

                // Function
                switch (v.VariableGenerator.GeneratingFunction) {

                case Conf.GeneratingFunctionType.Cosine:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Linear:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Ramp:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Sine:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.SquareWave:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Null:
                default:
                    seq = new float[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                    break;

                }

            } else {

                // Satic values
                seq = v.VariableGenerator.StaticValues;

            }

            return new VolumeSynthetizer(seq);
        }

    } /* VolumeSynthetizer */

    /// <summary>
    /// Represents a Synthesizer for Volume-typed variables.
    /// </summary>
    /// <remarks>
    /// This class cannot be constructed. Use the <c>GetInstanceMethod</c> instead.
    /// </remarks>
    public class TemperatureSynthetizer : Synthetizer {

        /// <summary>
        /// Construct a new synthetizer for type Temperature.
        /// </summary>
        private TemperatureSynthetizer() : base() {
        }

        /// <summary>
        /// Construct a new synthetizer for type Temperature specifying a sequence of values.
        /// </summary>
        /// <param name="seq">The specified sequence.</param>
        private TemperatureSynthetizer(float[] seq) : this() {
            this.sequence = seq;
        }

        /// <summary>
        /// Gets a new instance of the class.
        /// </summary>
        /// <param name="v">The variable used to set up the synthetizer.</param>
        /// <returns>A new instance of the class</returns>
        public static TemperatureSynthetizer GetInstance(Conf.VariableType v) {

            float[] seq;

            if (v.VariableGenerator.GeneratingFunctionSpecified) {

                // Function
                switch (v.VariableGenerator.GeneratingFunction) {

                case Conf.GeneratingFunctionType.Cosine:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Linear:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Ramp:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Sine:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.SquareWave:
                    seq = new float[] { 10, 3, 4, 3, 8, 2, 1, 0, 3, 4 };
                    break;

                case Conf.GeneratingFunctionType.Null:
                default:
                    seq = new float[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                    break;

                }

            } else {

                // Satic values
                seq = v.VariableGenerator.StaticValues;

            }

            return new TemperatureSynthetizer(seq);
        }

    } /* TemperatureSynthetizer */

} /* OCXSEnvironment */
